﻿using System.Text.RegularExpressions;

namespace BlogsAPI.Search
{
    using Abstract;
    using System.Web;
    using System.Collections.Generic;
    using System.Linq;
    using Newtonsoft.Json.Linq;
    using Newtonsoft.Json;
    using System.IO;
    using System.Net;
    using System.Diagnostics;
    using System;

    /// <summary>
    /// Класс для поиска в гугле 
    /// </summary>
    public class BlogsGoogle : BasicSearch
    {
        /// <summary>
        /// Конструктор
        /// </summary>
        public BlogsGoogle()
        {
            separator_and = " AND ";
            separator_or = " OR ";
            separator_not = "-";
        }

        /// <summary>
        /// Тип поиска по умолчанию
        /// </summary>
        public string searchType = "blogs";

        /// <summary>
        /// Версия поиска, а также все неучтенные параметры могут быть вставлены тут
        /// </summary>
        public string SearchApiVersion = "1.0";

        /// <summary>
        /// Настройки поисковика
        /// </summary>
        /// <remarks>Сортировка, отключение фильтра на порно, отключение фильтра дублей</remarks>
        public string SearchOptions = "&scoring=d&rsz=large&safe=off&filter=0";

        /// <summary>
        /// ссылка для запросов. scoring=d - сортировка по дате
        /// </summary>
        protected virtual string SearchBlogUrl
        {
            get
            {
                return "http://ajax.googleapis.com/ajax/services/search/{0}?v={1}{2}&start={3}&q={4}";
            }
        } 

        private const int MAXPAGES = 1;

        /// <summary>
        /// поиск в гугле-блогах
        /// </summary>
        /// <param name="text">текст для поиска</param>
        /// <param name="fromDate">дата начиная с которой искать объекты, может быть нулом, тогде по максимуму!</param>
        /// <returns>массив постов удовлетворяющих поиску</returns>
        public override BlogPost[] SearchFor(String text, DateTime? fromDate)
        {
            BlogPost[] oRes = new BlogPost[0];
            try
            {
                string searchUrl = String.Empty;
                if (!String.IsNullOrEmpty(text))
                {
                    for (int pageIndex = 0; pageIndex < MAXPAGES; pageIndex++)
                    {
                        // Append parameters
                        searchUrl = String.Format(
                            SearchBlogUrl,
                            searchType,
                            SearchApiVersion,
                            SearchOptions,
                            pageIndex,
                            HttpUtility.UrlEncode(text).Replace("&", "%26"));
                        string responseData = PerformRequest(searchUrl);
                        JObject googleSearch = JObject.Parse(responseData);

                        // get JSON result objects into a list
                        IList<JToken> results = googleSearch["responseData"]["results"].Children().ToList();
                        if (results.Count == 0) //если нет данных, то нет смысла мотать страницы дальше
                        {
                            break;
                        }
                        foreach (JToken result in results)
                        {
                            SearchResult searchResult = JsonConvert.DeserializeObject<SearchResult>(result.ToString());
                            if (searchResult == null) continue;
                            if (searchResult.publishedDate == DateTime.MinValue)
                                //когда поиск по вебу, то дата минимальна)
                            {
                                if (searchResult.postUrl.Contains("plus.google.com"))//то первые строчки в тексте и будут датой!
                                {
                                    Match m = Regex.Match(searchResult.content, @"^.{2,10}\d{4}\D",
                                                RegexOptions.Compiled | RegexOptions.IgnoreCase |
                                                RegexOptions.Singleline);
                                    if (m.Success)
                                    {
                                        DateTime dt;
                                        if (DateTime.TryParse(m.Groups[0].Value, out dt))
                                        {
                                            searchResult.publishedDate = dt;
                                        }
                                    }
                                }
                                if (searchResult.publishedDate == DateTime.MinValue)
                                {
                                    searchResult.publishedDate = DateTime.Now; //пускай будет сегодняшним днем
                                }
                            }
                            if (fromDate != null
                                     && searchResult.publishedDate < fromDate)
                            {
                                //ушли слишком далеко
                                pageIndex = MAXPAGES;
                                break; //прекращаем цикл
                            }
                            BlogPost bp = ParseBlogPost(searchResult);
                            if (bp != null)
                            {
                                Array.Resize(ref oRes, oRes.Length + 1);
                                oRes[oRes.Length - 1] = bp;
                            }
                        }
                    }
                }
            }
            catch(Exception e1)
            {
                Debug.WriteLine("SearchFor failed cause " + e1.Message);
            }
            return oRes;
        }


        /// <summary>
        /// поиск в гугле-блогах
        /// </summary>
        /// <param name="link">искомая ссылка на ресурс</param>
        /// <param name="fromDate">дата начиная с которой искать объекты, может быть нулом, тогде по максимуму!</param>
        /// <returns>массив постов удовлетворяющих поиску</returns>
        public override BlogPost[] SearchFor(Uri link, DateTime? fromDate)
        {
            return SearchFor("link:" + Common.DelHttp(link.OriginalString), fromDate);
        }

        /// <summary>
        /// Формирование разобранного поста из того что дает гугл.
        /// </summary>
        /// <param name="searchResult"></param>
        /// <returns></returns>
        private BlogPost ParseBlogPost(SearchResult searchResult)
        {
            if (searchResult.postUrl == null) return null;//нет ссылки, то и делать нечего
            return new BlogPost
                       {
                           //для гугла всегда лучше декодировать
                           Link = new Uri(HttpUtility.UrlDecode(searchResult.postUrl).Replace("http://www.google.com/url?q=","")),//иногда появляется фигня с гуглом, которую стоит отрезать
                           Body = searchResult.content,
                           Subject = searchResult.titleNoFormatting,
                           Existance = true, 
                           Published = searchResult.publishedDate

            };
        }

        private static string PerformRequest(string url)
        {
            WebRequest request = WebRequest.Create(url);

            using (WebResponse response = request.GetResponse())
            using (StreamReader reader = new StreamReader(response.GetResponseStream()))
            {
                return reader.ReadToEnd();
            }
        }

    }
}
